// +FHDR------------------------------------------------------------
//                 Copyright (c) 2024 NOVAUTO.
//                       ALL RIGHTS RESERVED
// -----------------------------------------------------------------
// Filename      : async_nbit_hand.v
// Author        : ICer
// Created On    : 2024-03-14 15:47
// Last Modified : 2024-03-14 17:04 by ICer
// -----------------------------------------------------------------
// Description:
//
//
// -FHDR------------------------------------------------------------


module async_nbit_hand #(
  parameter DL = 2,
  parameter WD = 1,
  parameter FF = 1
)( /*AUTOARG*/
   // Outputs
   i_ready, o_data, o_en,
   // Inputs
   i_clk, i_rst_n, i_data, i_en, o_clk, o_rst_n
   );

// ----------------------------------------------------------------
// Interface declare
// ----------------------------------------------------------------
input          i_clk;
input          i_rst_n;
input [WD -1:0]i_data;
input          i_en;
output         i_ready;

input          o_clk;
input          o_rst_n;
output[WD -1:0]o_data;
output         o_en;

// ----------------------------------------------------------------
// i_data dff
// ----------------------------------------------------------------
wire [WD -1:0]i_data_in;
wire          i_en_in;
generate
  if(FF == 0)begin: NO_IN_DFF
    assign i_data_in = i_data;
    assign i_en_in   = i_en;
  end //if(FF == 0)begin: NO_IN_DFF
  else begin: IN_DFF
    reg [WD -1:0]i_data_ff;
    reg          i_en_ff;
    always @(posedge i_clk or negedge i_rst_n) begin
      if(!i_rst_n)begin
        i_data_ff <= {WD{1'b0}};
        i_en_ff   <= 1'b0;
      end
      else begin
        i_data_ff <= i_data;
        i_en_ff   <= i_en;
      end
    end
    assign i_data_in = i_data_ff;
    assign i_en_in   = i_en_ff;
  end //else begin: IN_DFF
endgenerate

// ----------------------------------------------------------------
// tx enable logic
// ----------------------------------------------------------------
wire tx_en, tx_sel;
wire rx_sel, rx_sel_sync;
reg  tx_en_ff;

assign tx_en  = (i_en_in || tx_sel) && (!rx_sel_sync);
assign tx_sel = tx_en_ff;
always @(posedge i_clk or negedge i_rst_n) begin
  if(!i_rst_n)
    tx_en_ff <= 1'b0;
  else
    tx_en_ff <= tx_en;
end

// ----------------------------------------------------------------
// tx_sel rx_sel async
// ----------------------------------------------------------------
async_1bit_delay #(.DL(DL), .FF(0))
u_tx_sel_sync(
  .i_clk    (i_clk),
  .i_rst_n  (i_rst_n),
  .i_data   (tx_sel),
  .o_clk    (o_clk),
  .o_rst_n  (o_rst_n),
  .o_data   (rx_sel)
);

async_1bit_delay #(.DL(DL), .FF(0))
u_rx_sel_sync(
  .i_clk    (o_clk),
  .i_rst_n  (o_rst_n),
  .i_data   (rx_sel),
  .o_clk    (i_clk),
  .o_rst_n  (i_rst_n),
  .o_data   (rx_sel_sync)
);

// ----------------------------------------------------------------
// rx_sel_pulse
// ----------------------------------------------------------------
reg  rx_sel_ff, rx_sel_pulse_ff;
wire rx_sel_pulse;

always @(posedge o_clk or negedge o_rst_n) begin
  if(!o_rst_n)
    rx_sel_ff <= 1'b0;
  else
    rx_sel_ff <= rx_sel;
end

assign rx_sel_pulse = (rx_sel_ff == 1'b0) && (rx_sel == 1'b1);

always @(posedge o_clk or negedge o_rst_n) begin
  if(!o_rst_n)
    rx_sel_pulse_ff <= 1'b0;
  else
    rx_sel_pulse_ff <= rx_sel_pulse;
end

// ----------------------------------------------------------------
// i_data sample
// ----------------------------------------------------------------
reg [WD -1:0]i_data_lock;
always @(posedge i_clk or negedge i_rst_n) begin
  if(!i_rst_n)
    i_data_lock <= {WD{1'b0}};
  else if(i_en_in && i_ready)
    i_data_lock <= i_data_in;
end

reg [WD -1:0]i_data_sync;
always @(posedge o_clk or negedge o_rst_n) begin
  if(!o_rst_n)
    i_data_sync <= {WD{1'b0}};
  else if(rx_sel)
    i_data_sync <= i_data_lock;
end

// ----------------------------------------------------------------
// out logic
// ----------------------------------------------------------------
assign o_en    = rx_sel_pulse_ff;
assign o_data  = i_data_sync;

generate
  if(FF == 0)begin: NO_IN_DFF_RD
    assign i_ready = (tx_sel == 1'b0) && (rx_sel_sync == 1'b0);
  end
  else begin: IN_DFF_RD
    assign i_ready = (tx_sel == 1'b0) && (rx_sel_sync == 1'b0) && (i_en_in == 1'b0);
  end
endgenerate

endmodule
// Local Variables:
// verilog-auto-inst-param-value:t
// verilog-library-directories:(".")
// verilog-library-extensions:(".v")
// End:

